#include "stdafx.h"

DRAGO03::DRAGO03(float* input, float* lws, float lw_max, int w, int h, ScoreOperator sc)
{
	width = w;
	height = h;
	lwmax = lw_max;
	input_img = (float *) malloc(sizeof(float) * w * h * 3);
	memcpy(input_img, input, sizeof(float) * w * h * 3);
	lw_table = (float *) malloc(sizeof(float) * w * h);
	memcpy(lw_table, lws, sizeof(float) * w * h);
	sc_op = sc;
	SetDefaultParameter();
}

DRAGO03::~DRAGO03()
{
	free(input_img);
	free(lw_table);
}

float DRAGO03::GetBestValue(IplImage *img, int div_param, int round_param, float min_value, float max_value, float* __param, float* best_sc)
{
	float* params = new float[div_param];
	float inc, score, best_param = *__param;
	int i, j, round, mid_index;

	mid_index = div_param / 2;
	round = round_param;
	params[0] = min_value;
	params[div_param - 1] = max_value;
	inc = (params[div_param - 1] - params[0] + 1) / div_param;

	for (i = 1 ; i < div_param - 1 ; ++i)
		params[i] = params[i - 1] + inc;

	while (round)
	{
		for (i = 0 ; i < div_param ; ++i)
		{
			if (params[i] == INVALID_PARAM)
				continue;

			*__param = params[i];
			GetResult(img);
			score = sc_op(img);

			if (DEBUG_MSG)
				printf("params[%d] = %f, score = %f\r\n", i, params[i], score);

			if (score > *best_sc)
			{
				*best_sc = score;
				best_param = params[i];
			}
		}

		inc /= div_param;
		--round;

		// Preparing new params
		params[mid_index] = best_param;
		for (i = mid_index + 1 ; i < div_param ; ++i)
		{
			params[i] = params[i - 1] + inc;
			if (params[i] < min_value || params[i] > max_value)
			{
				for (j = i ; j < div_param ; ++j)
					params[j] = INVALID_PARAM;
				break;
			}
		}
		for (i = mid_index - 1 ; i >= 0 ; --i)
		{
			params[i] = params[i + 1] - inc;
			if (params[i] < min_value || params[i] > max_value)
			{
				for (j = i ; j >= 0 ; --j)
					params[j] = INVALID_PARAM;
				break;
			}
		}
 	}

	delete[] params;
	return best_param;
}

float DRAGO03::GetBestParameter(int divide_param, int round_param)
{
	IplImage *LDRImage = cvCreateImage(cvSize(width, height), 8, 3);
	float best_param, best_score;

	SetDefaultParameter();
	best_score = GetResult(LDRImage);

	// adjust exposure
	best_param = GetBestValue(LDRImage, divide_param, round_param, min_exposure, max_exposure, &exposure, &best_score);
	exposure = best_param;
	if (DEBUG_MSG)
		printf("\r\nbest param = %f, best score = %f\r\n\r\n", best_param, best_score);

	// free resources
	cvReleaseImage(&LDRImage);

	return best_score;
}

void DRAGO03::SetDefaultParameter()
{
	ldmax = default_ldmax;
	bias = default_bias;
	exposure = default_exposure;
	gamma_correction = default_gamma_correction;
}

void DRAGO03::SetParameter(float _lwmax, float _ldmax, float _bias, float _exposure, float _gamma_correction)
{
	if (_lwmax != 0.0f)
		lwmax = _lwmax;

	if (_ldmax != 0.0f)
		ldmax = _ldmax;

	if (_bias != 0.0f)
		bias = _bias;

	if (exposure != 0.0f)
		exposure = _exposure;

	if (_gamma_correction != 0.0f)
		gamma_correction = _gamma_correction;
}

void DRAGO03::PrintParameter()
{
	printf("------------------------------\r\n");
	printf("lwmax : %f\r\n", lwmax);
	printf("ldmax : %f\r\n", ldmax);
	printf("bias : %f\r\n", bias);
	printf("exposure : %f\r\n", exposure);
	printf("gamma_correction : %f\r\n", gamma_correction);
	printf("------------------------------\r\n");
}

float DRAGO03::GetResult(IplImage *output)
{
	int i, j, index;
	float r, g, b;
	float lw, n, m, p, ld;

	for (i = 0 ; i < height ; ++i)
	{
		for (j = 0 ; j < width ; ++j)
		{
			index = 3 * (i * width + j);

			r = input_img[index + 0];
			g = input_img[index + 1];
			b = input_img[index + 2];

			lw = lw_table[index / 3];
			n = ldmax * 0.01f * log(10.0f) / log(lwmax + 1.0f);
			m = log(lw + 1.0f);
			p = log(2.0f + (pow((lw / lwmax), log(bias) / log(0.5f)) * 8.0f));
			ld = n * m / p;

			r /= lw;
			r *= exposure;
			r = pow(r, gamma_correction);
			r *= (ld * 255.0f);

			g /= lw;
			g *= exposure;
			g = pow(g, gamma_correction);
			g *= (ld * 255.0f);

			b /= lw;
			b *= exposure;
			b = pow(b, gamma_correction);
			b *= (ld * 255.0f);

			if (r > 255)
				r = 255;
			if (r < 0)
				r = 0;
			if (g > 255)
				g = 255;
			if (g < 0)
				g = 0;
			if (b > 255)
				b = 255;
			if (b < 0)
				b = 0;

			CV_IMAGE_ELEM(output, uchar, i, j * 3 + 0) = uchar(b);
			CV_IMAGE_ELEM(output, uchar, i, j * 3 + 1) = uchar(g);
			CV_IMAGE_ELEM(output, uchar, i, j * 3 + 2) = uchar(r);
		}
	}

	return sc_op(output);
}

void DRAGO03::GetPixelResultGivenExposureAndLwMax(uchar& __r, uchar& __g, uchar& __b, float* fpInput, float __exposure, float __LwMax)
{
	float r, g, b;
	float lw, n, m, p, ld;

	r = fpInput[0];
	g = fpInput[1];
	b = fpInput[2];

	if (__LwMax < 0.05f)
		__LwMax = 0.05f;

	lw = 0.299f * r + 0.587f * g + 0.114f * b;
	n = default_ldmax * 0.01f * log(10.0f) / log(__LwMax + 1.0f);
	m = log(lw + 1.0f);
	p = log(2.0f + (pow((lw / __LwMax), log(default_bias) / log(0.5f)) * 8.0f));
	ld = n * m / p;

	r /= lw;
	r *= __exposure;
	r *= (ld * 255.0f);

	g /= lw;
	g *= __exposure;
	g *= (ld * 255.0f);

	b /= lw;
	b *= __exposure;
	b *= (ld * 255.0f);

	if (r > 255)
		r = 255;
	if (r < 0)
		r = 0;
	if (g > 255)
		g = 255;
	if (g < 0)
		g = 0;
	if (b > 255)
		b = 255;
	if (b < 0)
		b = 0;

	__r = (uchar) r;
	__g = (uchar) g;
	__b = (uchar) b;
}